package com.woniu.aop;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.woniu.annotation.RedisAnnotation;
import com.woniu.exception.DIYException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class RedisAspect {

    @Autowired
    private StringRedisTemplate template;

//    private RedisUtil redisUtil = new RedisUtil(template);

    private ObjectMapper mapper = new ObjectMapper();


    /**
     * 处理器
     * @return
     */
    @Around("@annotation(com.woniu.annotation.RedisAnnotation)")
    public Object manage(ProceedingJoinPoint point){
        try {
            //拿到对应的方法
            Method method = ((MethodSignature) point.getSignature()).getMethod();
            // 获取RedisAnnotation注解
            RedisAnnotation redisAnnotation = method.getAnnotation(RedisAnnotation.class);
            //判断注解对象是否为null
            if(redisAnnotation != null){
                //判断该方法是不是查询
                if(redisAnnotation.read()) {
                    log.info("查询业务");
                    //判断有没有key
                    String key;
                    if(redisAnnotation.value()==null || redisAnnotation.value().equals("")){
                        //没有key将用方法的全限定名当作key
                        key = method.getDeclaringClass().getName()+"."+method.getName();
                    }else key = redisAnnotation.value();

                    //用于存储传递参数作为hashKey
                    ArrayList<Object> list = new ArrayList<>();
                    for (Object arg : point.getArgs()) list.add(arg);
                    //如果没有传递参数，用key作为hashKey
                    if(list.size()==0) list.add(key);
                    String hashKey = mapper.writeValueAsString(list);
                    //判断是否存在key（redis）
                    if (template.hasKey(key)) {
                        //判断是否存在hashKey
                        if(template.opsForHash().hasKey(key,hashKey)){
                            //缓存返回
                            log.info("Redis取出");
                            Type type = method.getGenericReturnType();
                            return mapper.readValue((String) template.opsForHash().get(key, hashKey), mapper.getTypeFactory().constructType(type));
                        }
                    }else{
                        //初始化key
                        template.opsForHash().put(key,hashKey,"初始化");
                        template.expire(key,redisAnnotation.expired(),TimeUnit.SECONDS);
                    }
                    // Redis 中不存在，则从数据库中查找，并保存到 Redis
                    Object obj = point.proceed(point.getArgs());
                    String str = mapper.writeValueAsString(obj);
                    log.info("Redis存入");
                    template.opsForHash().put(key,hashKey,str);
                    return obj;
                }else{
                    //删除掉需要更新的key
                    template.delete(Arrays.asList(redisAnnotation.refresh()));
                    Object obj = point.proceed(point.getArgs());
                    return obj;
                }
            }
            throw new DIYException(-9999,"空指针异常");
        }catch (DIYException e){
            throw new DIYException(-9999,e.getMessage());
        }catch (Throwable e){
            e.printStackTrace();
            throw new DIYException(-9999,"服务器异常");
        }
    }

}
